package westh.ilib.service.surveyReport.reportGeneration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import westh.ilib.bean.surveyReport.RscTypeStat;
import westh.ilib.service.surveyReport.queryGeneration.Generator;
import westh.ilib.service.surveyReport.documentSearch.docSearcher;

import org.apache.lucene.search.Query;
import spiaotools.SentParDetector;

public class SummGenerator {

	// threshold to filter searched docs
	final float threshold = (float) 0.2;

	/*
	 * // compress ratio final float compressRatio = (float) 0.4;
	 */

	PorterStemmer stemmer = new PorterStemmer();

	// the class of Sentence
	class Sentence {
		String text;

		// sentence location
		int paraPos;
		int sentPos;

		// the sequence num of this sentence been choosed
		int choosedNum;

		// score computed by sentScorer()
		double score;

		// score computed by sentClassifier()
		double classScore;

		// score computed by sentSimilarity() -- (title-sent similarity)
		double simScore;

		// score computed by sentLocation()
		double locationScore;

		// words after stemming, using Arrays.toString() to concate
		String words;
	};

	// class of document
	class Document {
		// score, year, conf, title, keyword from docSearcher.search()
		float score;
		int year;
		String conf;
		String title;
		String keyword;
		int rscType;

		// sentences of summary
		ArrayList<Sentence> sents;

		// selected sequence of sentences to form multi-document summary
		int[] selected;

		// the highest-scored sentence
		int firstSent;
	};

	// docs which summary generated from
	List<Document> docs;

	// summary generated finally
	String summary;

	// selected attributes after stemming by Porter Stemmer
	/*
	 * static String[] attributes = { "we", "paper", "work", "show", "present",
	 * "describe", "propos", "introduc", "design", "implement", "adopt", "us",
	 * "demostr", "algorithm", "approach", "find", "method", "techniqu",
	 * "model"};
	 */
	// negative attributes which indicates experimental result strongly
	static String[] negAttributes = { "evalu", "estim", "experiment" };

	public SummGenerator(int n, float[] scores, String[] years, String[] confs,
			String[] titles, String[] keywords, String[] summarys, int[] rscType) {
		try {
			docs = new ArrayList<Document>();
			for (int i = 0; i < n; i++) {
				Document doc = new Document();
				if (titles[i] == null || scores[i] < threshold) {
					break;
				}
				doc = new Document();
				doc.score = scores[i];
				doc.year = Integer.parseInt(years[i]);
				doc.conf = confs[i];
				doc.title = titles[i];
				doc.keyword = keywords[i];
				doc.rscType = rscType[i];
				doc.sents = new ArrayList<Sentence>();
				docs.add(i, doc);
				sentSegmenter(i, summarys[i]);
			}
			System.out.println(docs.size());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// segment doc into sents & store in docs
	// use the 3rd party software sptoolkit.jar
	private void sentSegmenter(int index, String doc) {
		SentParDetector detector = new SentParDetector();
		if (doc == null) {
			return;
		}
		String segmentResult = detector.markupRawText(0, doc);

		/*
		 * parse the result returned by sptoolkit.jar markupRawText the format
		 * is: ^ sentence 1^ sentence 2^ ……^ sentence n^ ^ paragraph 2^ ……^
		 */
		String[] sents = segmentResult.split("\n");

		int paraNum = 0;
		int sentNum = 0;
		int k = 0;
		for (int j = 0; j < sents.length; j++) {
			// the line with only a '^' stands for a new paragraph
			if (sents[j].length() == 1) {
				if (sents[j].charAt(0) == '^') {
					paraNum++;
					sentNum = 0;
				}
				continue;
			}

			Sentence sent = new Sentence();
			// the last character of a sentence is '^' too
			sent.text = sents[j].substring(0, sents[j].length() - 1);
			if (sent.text.compareToIgnoreCase("nul") == 0) {
				continue;
			} else {
				sent.text = sent.text.replaceAll("- ", "");
			}
			sent.paraPos = paraNum;
			sent.sentPos = ++sentNum;
			this.docs.get(index).sents.add(k++, sent);
		}
	}

	// sort the documents based on 1)year ascending and 2)score descending
	private void docSorter() {
		Collections.sort(docs, new Comparator() {
			public int compare(Object o1, Object o2) {
				Document doc1 = (Document) o1;
				Document doc2 = (Document) o2;

				// sort doc that published earlier ahead
				if (doc1.year != doc2.year) {
					return (doc1.year - doc2.year);
				}
				// sort docs score higher ahead
				else {
					if (doc1.score > doc2.score) {
						return -1;
					} else if (doc1.score < doc2.score) {
						return 1;
					}
				}
				return 0;
			}
		});
	}

	private void sentClassifier() {
		/*
		 * weka.functions.SGD Loss function: Hinge loss (SVM) class = -0.01
		 * (normalized) we + 2.1389 (normalized) paper + -0.02 (normalized) work
		 * + -0.03 (normalized) show + -0.1 (normalized) present + 0
		 * (normalized) describ + 2.0091 (normalized) propos + 1.9791
		 * (normalized) introduc + 0.02 (normalized) design + 0.09 (normalized)
		 * implement + 1.9692 (normalized) adopt + 0.02 (normalized) us + 0
		 * (normalized) demostr + 0.04 (normalized) algorithm + 0.01
		 * (normalized) approach + 2.0091 (normalized) find + 0.01 (normalized)
		 * method + -0.02 (normalized) techniqu + 2.029 (normalized) model +
		 * -0.01 (normalized) evalu + -0.08 (normalized) estim - 0.99
		 */
		double[] weight = { -0.01, 2.1389, -0.02, -0.03, -0.1, 0, 2.0091,
				1.9791, 0.02, 0.09, 1.9692, 0.02, 0, 0.04, 0.01, 2.0091, 0.01,
				-0.02, 2.029, -0.01, -0.08, 0.99 };

		/*
		 * weka.functions.SimpleLogistic Class 1 : -0.56 + [paper] * 1.59 +
		 * [show] * -0.29 + [propos] * 0.75 + [introduc] * 0.9 + [adopt] * 1.51
		 * + [find] * 0.74 + [model] * 0.87
		 * 
		 * double[] weight = { 0, 1.59, 0, -0.29, 0, 0, 0.75, 0.9, 0, 0, 0,
		 * 1.51, 0, 0, 0, 0, 0.74, 0, 0, 0.87, 0, -0.56 };
		 */
		for (int i = 0; i < docs.size(); i++) {
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				// get the attributes vector of a sentence
				boolean[] vector = ClassifierTraining.getAttributeVector(docs
						.get(i).sents.get(j).text);

				// compute classification score
				docs.get(i).sents.get(j).classScore = 0;
				for (int k = 0; k < vector.length; k++) {
					if (vector[k]) {
						docs.get(i).sents.get(j).classScore += weight[k];
					}
				}
				docs.get(i).sents.get(j).classScore /= Math.sqrt(vector.length);
				// docs.get(i).sents.get(j).classScore -= weight[vector.length];
			}
		}
	}

	// compute cosine similarity between title and summary sentences
	private void sentSimilarity() {
		for (int i = 0; i < docs.size(); i++) {
			String[] titleWords = docs.get(i).title.split(" ");
			titleWords = stemmer.stemWords(titleWords);
			Arrays.sort(titleWords);

			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				String[] sentWords = docs.get(i).sents.get(j).text.split(" ");
				sentWords = stemmer.stemWords(sentWords);
				Arrays.sort(sentWords);

				// compute cosine similarity between title & sent
				int sum = 0;
				for (int s = 0; s < titleWords.length; s++) {
					for (int t = 0; t < sentWords.length; t++) {
						if (titleWords[s].compareToIgnoreCase(sentWords[t]) == 0) {
							sum++;
						}
					}
				}
				docs.get(i).sents.get(j).simScore = (double) sum
						/ (Math.sqrt(titleWords.length) * Math
								.sqrt(sentWords.length));
				docs.get(i).sents.get(j).words = Arrays.toString(sentWords)
						.toLowerCase();
			}
		}
	}

	// sentences location indicates the sentences show experimental results
	private void sentLocation() {
		for (int i = 0; i < docs.size(); i++) {
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				for (int k = 0; k < negAttributes.length; k++) {
					if (docs.get(i).sents.get(j).words
							.indexOf(negAttributes[k]) != -1) {
						docs.get(i).sents.get(j).locationScore = 1;
						break;
					}
				}
			}
		}
	}

	// compute sentence score with simScore, classScore, locationScore
	private void sentScorer() {
		sentClassifier();
		sentSimilarity();
		sentLocation();

		// ParameterTraining.training(docs);

		for (int i = 0; i < docs.size(); i++) {
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				docs.get(i).sents.get(j).score = docs.get(i).sents.get(j).simScore
						* 0.6
						+ docs.get(i).sents.get(j).classScore
						* 0.4
						+ (-1) * docs.get(i).sents.get(j).locationScore;
			}
		}
	}

	// select sentences to form summary
	// choose only the first one
	private void sentSelector() {
		for (int i = 0; i < docs.size(); i++) {
			// for docs that summary is empty
			if(docs.get(i).sents.size() == 0){
				docs.get(i).firstSent = -1;
				continue;
			}
			
			// sort the sentences according to score descending
			int index = 0;
			double highScore = -1.0;
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				double score = docs.get(i).sents.get(j).score;
				if (score > highScore) {
					highScore = score;
					index = j;
				}
			}
			docs.get(i).firstSent = index + 1;
		}
	}

	// first sort & get the pick-up sequence
	private void sentSelector2() {
		final class Pair {
			int index;
			double score;
		}
		;
		for (int i = 0; i < docs.size(); i++) {
			Pair[] sentScorePair = new Pair[docs.get(i).sents.size()];
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				sentScorePair[j] = new Pair();
				sentScorePair[j].index = j;
				sentScorePair[j].score = docs.get(i).sents.get(j).score;
			}
			Arrays.sort(sentScorePair, new Comparator() {
				public int compare(Object o1, Object o2) {
					double sub = ((Pair) o1).score - ((Pair) o2).score;
					if (sub < 0) {
						return 1;
					} else if (sub > 0) {
						return -1;
					}
					return 0;
				}
			});
			docs.get(i).selected = new int[docs.get(i).sents.size()];
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				docs.get(i).sents.get(sentScorePair[j].index).choosedNum = j + 1;
			}
		}
	}

	// TODO: smoothing between the selected sentences, eg. pronoun,
	// "this paper", "in the paper", "this poster"
	private void smoother() {
		summary = "";
		for (int i = 0; i < docs.size(); i++) {
			if(docs.get(i).firstSent == -1){
				continue;
			}
			summary = summary.concat("[" + (i + 1) + "] "
					+ docs.get(i).sents.get(docs.get(i).firstSent - 1).text
					+ "\n\r");
		}
	}

	// print out all the docs & sents for test
	public void displayDocs() {
		for (int i = 0; i < docs.size(); i++) {
			System.out.println("doc " + i);
			System.out.println(docs.get(i).year + "\t" + docs.get(i).conf
					+ "\t" + docs.get(i).score + "\t" + docs.get(i).title);
			System.out.println("sent num: " + docs.get(i).sents.size());
			System.out.println("selected num: " + docs.get(i).firstSent);
			for (int j = 0; j < docs.get(i).sents.size(); j++) {
				System.out.println(docs.get(i).sents.get(j).choosedNum + " "
						+ docs.get(i).sents.get(j).sentPos + " "
						+ docs.get(i).sents.get(j).score + "\t\t"
						+ docs.get(i).sents.get(j).locationScore + "\t"
						+ docs.get(i).sents.get(j).simScore + "\t\t"
						+ docs.get(i).sents.get(j).classScore + " "
						+ docs.get(i).sents.get(j).text);
			}
		}

		System.out.println();
		System.out.println();
		System.out.println("summary finally!");
		System.out.println(summary);
	}

	public String generator() {
		docSorter();
		sentScorer();
		sentSelector();
		smoother();
		return summary;
	}

	public RscTypeStat calculate() {
		RscTypeStat stat = new RscTypeStat();
		// stat.confNum = summGen.docs.size();
		for (int i = 0; i < docs.size(); i++) {
			switch (docs.get(i).rscType) {
			case 1:
				stat.bookNum++;
				break;
			case 2:
				stat.confNum++;
				break;
			case 3:
				stat.journalNum++;
				break;
			case 4:
				stat.patentNum++;
				break;
			case 5:
				stat.softwCopyrNum++;
				break;
			case 6:
				stat.standardNum++;
				break;
			case 7:
				stat.techReportNum++;
				break;
			default:
				stat.confNum++;
				break;
			}
		}
		return stat;
	}

	public static void main(String[] args) {
		/* unit test */
		// directory path of input data docs
		final String xmlPath = "E:\\zhyx\\SurveyReport\\xml\\";

		// directory path of index
		final String indexPath = "E:\\zhyx\\SurveyReport\\index\\";

		// directory of index of wordNet synonyms
		final String wordnetIndexPath = "E:\\zhyx\\SurveyReport\\wordnetIndex";

		int n = 50;
		float[] scores = new float[n];
		String[] years = new String[n];
		String[] confs = new String[n];
		String[] titles = new String[n];
		String[] keywords = new String[n];
		String[] summarys = new String[n];
		int[] rscType = new int[n];

		Generator gen = new Generator("summarization");
		Query query = gen.generator(wordnetIndexPath);
		docSearcher.search(indexPath, query, n, scores, years, confs, titles,
				keywords, summarys, rscType);

		SummGenerator summGen = new SummGenerator(n, scores, years, confs,
				titles, keywords, summarys, rscType);
		
		summGen.generator();
		summGen.displayDocs();
	}
}
